package game.server.world.rtree;

import game.server.world.GameObject;
import game.server.world.GameObjectVisitor;
import game.server.world.GameWorld;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import rtree.ABL;
import rtree.IllegalValueException;
import rtree.Point;
import rtree.RTree;
import rtree.RTreeException;
import rtree.RTreeInsertException;

public class RTreeWorld implements GameWorld{
	
	private RTree rtree;
	private String rtreeFileName = "rtree.txt";

	private Map<GameObject,RTreeObject> mapByGo = new HashMap<GameObject, RTreeObject>();
	private Map<Long,RTreeObject> mapByPtr = new TreeMap<Long, RTreeObject>();
	
	private int objectCount = 0;
	
	public RTreeWorld() throws RTreeException {
		rtree = new RTree(rtreeFileName);
	}
	
	@Override
	public synchronized void addObject(GameObject obj) {
		try {
			RTreeObject rtreeobject = new RTreeObject(rtree);
			rtreeobject.setObject(obj);
			rtreeobject.addToWorld();
			
			mapByGo.put(obj, rtreeobject);
			mapByPtr.put(rtreeobject.getPtr(), rtreeobject);
			
			++objectCount;
		} catch (RTreeInsertException e) {
			e.printStackTrace();
		} catch (IllegalValueException e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void removeObject(GameObject obj) {
		try {
			RTreeObject ro = mapByGo.get(obj);
			ro.deleteFromWorld();
			mapByPtr.remove(ro.getPtr());
			mapByGo.remove(obj);
			--objectCount;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void update() {
		for (RTreeObject object : mapByGo.values()){
			if (object.needUpdate()){
				try {
					object.updatePosition();
				} catch (Exception e) {
					e.printStackTrace();
					System.exit(1);
				}
			}
		}
	}

	@Override
	public void visitObjectsAtRange(float x, float y, int range,GameObjectVisitor visitor) {
		try {
			long ptr = -1;
			for (ABL abl : rtree.nearestSearch(new Point((int)x, (int)y), (long)Math.pow(range, 2), objectCount)){
				if (abl != null && abl.element != null && mapByPtr.containsKey(ptr = abl.element.getPtr())){
					visitor.visit(mapByPtr.get(ptr).getObject());
				}
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public String toString(){
		return "RTreeWorld " + objectCount;
	}
}
